/*******************************************************************************
* Copyright (c) 2009 Andrey Loskutov.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* Contributor: Andrey Loskutov - initial API and implementation
*******************************************************************************/
package de.loskutov.anyedit.compare;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.compare.CompareConfiguration;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.CompareViewerSwitchingPane;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.contentmergeviewer.TextMergeViewer;
import org.eclipse.compare.structuremergeviewer.DiffNode;
import org.eclipse.compare.structuremergeviewer.Differencer;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.progress.UIJob;
import de.loskutov.anyedit.AnyEditToolsPlugin;
import de.loskutov.anyedit.util.EclipseUtils;
/**
* @author Andrey
*/
/*implements IContentChangeNotifier*/
public class AnyeditCompareInput extends CompareEditorInput {
static class ExclusiveJobRule implements ISchedulingRule {
private final Object id;
public ExclusiveJobRule(Object id) {
this.id = id;
}
@Override
public boolean contains(ISchedulingRule rule) {
return rule instanceof ExclusiveJobRule && ((ExclusiveJobRule) rule).id == id;
}
@Override
public boolean isConflicting(ISchedulingRule rule) {
return contains(rule);
}
}
// org.eclipse.compare.internal.CompareEditor.CONFIRM_SAVE_PROPERTY;
private static final String CONFIRM_SAVE_PROPERTY = "org.eclipse.compare.internal.CONFIRM_SAVE_PROPERTY";
private StreamContent left;
private StreamContent right;
private Object differences;
/**
* allow "no diff" result to keep the editor open
*/
private boolean createNoDiffNode;
public AnyeditCompareInput(StreamContent left, StreamContent right) {
super(new CompareConfiguration());
this.left = left;
this.right = right;
getCompareConfiguration().setProperty(CONFIRM_SAVE_PROPERTY, Boolean.FALSE);
}
@Override
public Object getAdapter(Class adapter) {
if(IFile.class == adapter) {
Object object = EclipseUtils.getIFile(left, false);
if(object != null) {
return object;
}
return EclipseUtils.getIFile(right, false);
}
return super.getAdapter(adapter);
}
@Override
protected Object prepareInput(IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
if (right == null || left == null) {
return null;
}
try {
initTitle();
left.init(this);
right.init(this);
Differencer differencer = new Differencer();
String message = "Comparing " + left.getName() + " with " + right.getName();
monitor.beginTask(message, 30);
IProgressMonitor sub = new SubProgressMonitor(monitor, 10);
try {
sub.beginTask(message, 100);
differences = differencer.findDifferences(false, sub, null, null, left, right);
if(differences == null && createNoDiffNode) {
differences = new DiffNode(left, right);
}
return differences;
} finally {
sub.done();
}
} catch (OperationCanceledException e) {
left.dispose();
right.dispose();
throw new InterruptedException(e.getMessage());
} finally {
monitor.done();
}
}
@Override
public void saveChanges(IProgressMonitor monitor) throws CoreException {
super.saveChanges(monitor);
if (differences instanceof DiffNode) {
try {
boolean result = commit(monitor, (DiffNode) differences);
// let the UI re-compare here on changed inputs
if(result){
reuseEditor();
}
} finally {
setDirty(false);
}
}
}
@Override
public Object getCompareResult() {
Object compareResult = super.getCompareResult();
if(compareResult == null){
// dispose???
}
return compareResult;
}
void reuseEditor() {
UIJob job = new UIJob("Updating differences"){
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if(monitor.isCanceled() || left.isDisposed() || right.isDisposed()){
return Status.CANCEL_STATUS;
}
// This causes too much flicker:
// AnyeditCompareInput input = new AnyeditCompareInput(left.recreate(), right
// .recreate());
// if(monitor.isCanceled()){
// input.internalDispose();
// return Status.CANCEL_STATUS;
// }
// CompareUI.reuseCompareEditor(input, (IReusableEditor) getWorkbenchPart());
AnyeditCompareInput input = AnyeditCompareInput.this;
// allow "no diff" result to keep the editor open
createNoDiffNode = true;
try {
StreamContent old_left = left;
left = old_left.recreate();
old_left.dispose();
StreamContent old_right = right;
right = old_right.recreate();
old_right.dispose();
// calls prepareInput(monitor);
input.run(monitor);
if(differences != null){
CompareViewerSwitchingPane pane = getInputPane();
if(pane != null){
Viewer viewer = pane.getViewer();
if (viewer instanceof TextMergeViewer) {
viewer.setInput(differences);
}
}
}
} catch (InterruptedException e) {
// ignore, we are interrupted
return Status.CANCEL_STATUS;
} catch (InvocationTargetException e) {
AnyEditToolsPlugin.errorDialog("Error during diff of " + getName(), e);
return Status.CANCEL_STATUS;
} finally {
createNoDiffNode = false;
}
return Status.OK_STATUS;
}
@Override
public boolean belongsTo(Object family) {
return AnyeditCompareInput.this == family;
}
};
job.setPriority(Job.SHORT);
job.setUser(true);
job.setRule(new ExclusiveJobRule(this));
Job[] jobs = Job.getJobManager().find(this);
if(jobs.length > 0){
for (int i = 0; i < jobs.length; i++) {
jobs[i].cancel();
}
}
jobs = Job.getJobManager().find(this);
if(jobs.length > 0) {
job.schedule(1000);
} else {
job.schedule(500);
}
}
public CompareViewerSwitchingPane getInputPane() {
try {
Field field = CompareEditorInput.class.getDeclaredField("fContentInputPane");
field.setAccessible(true);
Object object = field.get(this);
if(object instanceof CompareViewerSwitchingPane) {
return (CompareViewerSwitchingPane) object;
}
} catch (Throwable e) {
// ignore
}
return null;
}
private boolean commit(IProgressMonitor monitor, ICompareInput diffNode) {
boolean okLeft = commitNode(monitor, diffNode.getLeft());
boolean okRight = commitNode(monitor, diffNode.getRight());
return okLeft || okRight;
}
private boolean commitNode(IProgressMonitor monitor, ITypedElement element) {
if(element instanceof StreamContent) {
StreamContent content = (StreamContent) element;
if(content.isDirty()) {
try {
return content.commitChanges(monitor);
} catch (CoreException e) {
AnyEditToolsPlugin.errorDialog("Error during save of " + content.getName(), e);
}
}
}
return false;
}
private void initTitle() {
CompareConfiguration cc = getCompareConfiguration();
String nameLeft = left.getName();
String nameRight = right.getName();
if(nameLeft.equals(nameRight)){
nameLeft = left.getFullName();
nameRight = right.getFullName();
}
cc.setLeftLabel(nameLeft);
cc.setLeftImage(left.getImage());
cc.setRightLabel(nameRight);
cc.setRightImage(right.getImage());
cc.setLeftEditable(true);
cc.setRightEditable(true);
setTitle("Compare (" + nameLeft + " - " + nameRight + ")");
}
@Override
protected void handleDispose() {
internalDispose();
super.handleDispose();
}
public void internalDispose() {
left.dispose();
right.dispose();
differences = null;
}
}